package RMISnake;

//import ClientInterface;

import java.io.Serializable;
import java.rmi.*;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.*;

import javax.swing.JOptionPane;

import RMISnake.Node;

public class SnakeServerImp extends UnicastRemoteObject implements SnakeServer {
	public final static int SUCCESS = 0;
	public final static int ERR_USER_NAME = 1;
	public final static int QUIT = 1;
	public final static int MSG = 2;
	public final static int JOIN = 3;
	public final static int START = 4;

	public final static String SERVER = "";

	Hashtable snakePlayers = new Hashtable();
	Hashtable snakes = new Hashtable();
	boolean foodOn = false;

	public SnakeServerImp() throws RemoteException {
	}

	// join in the game.
	public int addSnakeListener(CallBack cl, String userName)
			throws java.rmi.RemoteException {

		// Check for collision
		if (snakePlayers.get(userName) != null) {
			System.out.println("Duplicate user! (" + userName + ")");
			JOptionPane.showMessageDialog(null, "The " + userName
					+ " has signed in!", "Signin Error",
					JOptionPane.INFORMATION_MESSAGE);
			//System.exit(0);
			return ERR_USER_NAME;
		} else if (userName.equals(SERVER)) {
			System.out.println("Bad username.");
			return ERR_USER_NAME;
		} else {
			// add new user into the hashtable
			snakes.put(userName, new Snake(snakes.size()));
			snakePlayers.put(userName, cl);
			sendSnakeEvent(JOIN, userName, SERVER);
			return SUCCESS;
		}
	}

	// exit game
	public void removeSnakeListener(String userName, String reason)
			throws java.rmi.RemoteException {
		sendSnakeEvent(QUIT, userName, reason);
		snakePlayers.remove(userName);
		System.out.println("User quit: " + userName);
	}

	// send msg between players
	public void sendSnakeMessage(String sender, String message)
			throws java.rmi.RemoteException {
		sendSnakeEvent(MSG, sender, message);
	}

	/*
	 * sendSnakeEvent is called by clients indirectly through other methods for
	 * communication purposes. send msg based on different type value.
	 */
	public void sendSnakeEvent(int type, String sender, String message) {

		Hashtable h;

		synchronized (this) {
			h = (Hashtable) snakePlayers.clone();
		}

		/*
		 * Disallow further communication from someone who has been removed from
		 * the table.
		 */
		// This is for test below.
		System.out.println("sender:" + sender);
		if (snakePlayers.get(sender) != null) {
			for (Enumeration e = h.elements(); e.hasMoreElements();)
				System.out.println(e.nextElement());
		}
		if (sender.equals(SERVER) || snakePlayers.get(sender) != null) {

			try {
				for (Enumeration e = h.elements(); e.hasMoreElements();) {
					CallBack cl = (CallBack) e.nextElement();

					switch (type) {
					case START:
						// This is for test.
						System.out.println("aa");
						// this.startDraw(cl);
						DrawThread draw = new DrawThread(cl);
						draw.start();
						break;
					case MSG:
						cl.addMessage(sender, message);
						break;
					case JOIN:
						cl.joinedMessage(sender);
						break;
					case QUIT:
						cl.quitMessage(sender);
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		} else {
			System.out.println("bbb");
		}
	}

	public void startDraw(CallBack cl) {
		DrawThread draw = new DrawThread(cl);
		draw.start();
		/*
		 * (new Thread() { Random r = new Random(); public void run() { try {
		 * this.sleep(r.nextInt(10000)); } catch (InterruptedException e) { //
		 * TODO Auto-generated catch block e.printStackTrace(); } if(!foodOn)
		 * createFood(); } }).start();
		 */
	}

	public static void main(String argv[]) {
		try {
			SnakeServerImp thisExample = new SnakeServerImp();
			// create a rmi remote obj
			Registry reg = LocateRegistry.createRegistry(12345);
			reg.rebind("//localhost/SnakeServer:9091", thisExample);
			System.out.println("Server Bound to RMIRegistry");
		} catch (Exception e) {
			System.out.println("SnakeServer err: " + e.getMessage());
			e.printStackTrace();
		}
	}

	/*
	 * Random a food
	 */
	public void createFood() {

		int x = 0;
		int y = 0;
		try {
			Random r = new Random();
			x = r.nextInt(600 / 10);
			y = r.nextInt(400 / 10);
			// isFood[x][y] = true;
			// food = new Node(x, y);
		} catch (Exception e) {
			e.printStackTrace();
		}
		snakes.put("FOOD", new Node(x, y));
	}

	class DrawThread extends Thread {
		CallBack c;

		public DrawThread(CallBack cl) {
			this.c = cl;
		}

		public void run() {
			synchronized (snakes) {
				while (true) {
					try {
						c.repaint(snakes);
						Thread.sleep(100);
					} catch (RemoteException | InterruptedException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
				}
			}
		}
	}
}
